/*
Kiwi Editor is a framework for building WYSIWYG web editors.
Copyright (C) 2011  Roosevelt de Lima Sardinha

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

Contact the author at roosevelt1@gmail.com (Roosevelt) 
*/

/**
 * The box where objects are edited, i.e., where you put objects, move and change its properties.
 * @author Roosevelt de Lima Sardinha
 * @param {String} id The unique id of the edition area
 * @param {Object} properties The object containing the properties to be initially set to the edition area.
 * @constructor
 */
function editionArea(id, properties){

    /**
	 * The workspace this edition area belongs to.
	 * @type {Object}  
	 */
	this.workspace = properties.workspace;
	
	/**
	 * The unique id of the property box.
	 * @type {String}
	 */
	this.id = id;
    var thisObject = this; 
	
	/**
	 * The object that contains the initial properties of the edition area.
	 * @type {Object} 
	 */	
	this.properties = properties;
	
	/** 
	 * The id of the DOM node that contains this edition area.
	 * @type {String}
	 */	
	this.parentId = this.properties.parent;
	
	/**
	 * The path of the CSS stylesheet file.
	 * @type {String}
	 */	
	this.cssPath = this.properties.cssPath ? this.properties.cssPath : "css/boxes/editionArea.css";
	
	/**
	 * The title of this edition area.
 	 * @type {String}
	 */	
	this.title = this.properties.title ? this.properties.title : 'EditionArea';
	
	/**
	 * The left coordinate of the edition area.
	 * @type {Int}
	 */		
	this.left = this.properties.left ? parseInt(this.properties.left) : 0;
	
	/**
	 * The top coordinate of the edition area.
	 * @type {Int}
	 */	
	this.top = this.properties.top ? parseInt(this.properties.top) : 0;
	
	/**
	 * The width of the edition area.
	 * @type {Int}
	 */
	this.width = this.properties.width ? parseInt(this.properties.width) : 100;
	
	/**
	 * The height of the edition area.
	 * @type {Int}
	 */		
	this.height = this.properties.height ? parseInt(this.properties.height) : 100;
	
	// Events
	/**
	 * <b>Event</b><br/>
	 * Defines what is going to be done when you drag the edition area.
	 * @param {Int} x The left coordinate of the edition area while dragging.
	 * @param {Int} y The top coordinate of the edition area while dragging.
	 * @param {Object} editionArea The edition area being dragged.
	 */			
	this.onDrag = function(x, y, editionArea){};
	
	/**
	 * <b>Event</b><br/>
	 * Defines what is going to be done when you drop the edition area.
	 * @param {Int} x The left coordinate of the edition area when dropped.
	 * @param {Int} y The top coordinate of the edition area when dropped.
	 * @param {Object} editionArea The edition area being dropped.
	 */		
	this.onDrop = function(x, y, editionArea){};
	
	/**
	 * <b>Event</b><br/>
	 * Defines what is going to be done when you start to resize the edition area.
	 * @param {Int} width The width of the edition area when you start to resize.
	 * @param {Int} height The height of the edition area when you start to resize.
	 * @param {Object} editionArea The edition area being resized.
	 */		
	this.onBeforeResize = function(width, height, editionArea){};
	
	/**
	 * <b>Event</b><br/>
	 * Defines what is going to be done during resize.
	 * @param {Int} width The width of the edition area during resize.
	 * @param {Int} height The height of the edition area during resize.
	 * @param {Object} editionArea The edition area being resized.
	 */	
	this.onResize = function(width, height, editionArea){};
	
	/**
	 * <b>Event</b><br/>
	 * Defines what is going to be done after resize.
	 * @param {Int} width The width of the edition area after resize.
	 * @param {Int} height The height of the edition area after resize.
	 * @param {Object} editionArea The edition area resized.
	 */		
	this.onAfterResize = function(width, height, editionArea){};
	
	/**
	 * <b>Event</b><br/>
	 * Defines what is going to be done when you remove this edition area.
	 */	
	this.onRemove = function(){};
	
	/**
	 * <b>Event</b><br/>
	 * Defines what is going to be done when you restore the size of this edition area.
	 * @param {Object} editionArea The edition area the size was restored.
	 */		
	this.onRestoreSize = function(editionArea){};
	
	/**
	 * <b>Event</b><br/>
	 * Called when an EO id appended to the edition area.
	 * @param {Object} EO The EO being appended.
	 * @param {Object} editionArea The edition area that appended the EO.
	 */
	this.onAppend = function(EO, editionArea){};
		
    var parent = document.getElementById(properties.parent);
    
    // Gets the initial size and position of the object
    var iniLeft = parseInt(this.properties.left);
    var iniTop = parseInt(this.properties.top);
    var iniWidth = parseInt(this.properties.width);
    var iniHeight = parseInt(this.properties.height);

    var editionAreaBg = document.createElement('div');
    editionAreaBg.id = this.id + 'Bg';
    editionAreaBg.className = 'edition-area-box';
	editionAreaBg.style.position = 'absolute';
	editionAreaBg.style.borderWidth = '4px';
    editionAreaBg.style.left = this.left + 'px';
    editionAreaBg.style.top = this.top + 'px';
    editionAreaBg.style.width = this.width + 'px';
    editionAreaBg.style.height = this.height + 'px';
    
    //code for the divs: background of the object and the div where the user works		
    var editionArea = document.createElement('div');
	/**
	 * @ignore
	 */
    editionArea.id = this.id;
	/**
	 * @ignore
	*/
	editionArea.className = 'edition-area-content';
	editionArea.style.position = 'absolute';
    editionArea.style.left = this.left + parseInt(editionAreaBg.style.borderWidth) + 'px';
    editionArea.style.top = this.top + parseInt(editionAreaBg.style.borderWidth) + 'px';
    editionArea.style.width = this.width + 'px';
    editionArea.style.height = this.height + 'px';

	
    // A copy of the background div is generate to solve problems like opacity heritance and borders size
    var editionAreaHeader = document.createElement('div');
    editionAreaHeader.id = this.id + 'Header';
    editionAreaHeader.className = 'edition-area-header';
    editionAreaHeader.innerHTML = this.title;
	
	editionArea.appendChild(editionAreaHeader);
    
    parent.appendChild(editionAreaBg);
	parent.appendChild(editionArea);

	/**
	 * The EOM to manage EOs in this edition area.
	 * @type {Object}
	 */
    this.EOM = new editorObjectManager(this);
	
    editionAreaHeader.ondblclick = function(){
        thisObject.setLeft(iniLeft);
        thisObject.setTop(iniTop);
        var maxRight = thisObject.EOM.getMaxRight();
        if (maxRight < iniWidth) {
            thisObject.setWidth(iniWidth);
        }
        else {
            thisObject.setWidth(maxRight);
        }
        
        var maxBottom = thisObject.EOM.getMaxBottom();
        if (maxBottom < iniHeight) {
            thisObject.setHeight(iniHeight);
        }
        else {
            thisObject.setHeight(maxBottom);
        }
		
		thisObject.onRestoreSize(thisObject);
    };
    
    //make the divs resizable
    var maxRight = 0;
    var maxBottom = 0;
    $('#' + this.id).resizable({
        start: function(event, ui){
			thisObject.onBeforeResize(document.getElementById(thisObject.id).offsetWidth, document.getElementById(thisObject.id).offsetHeight, thisObject);
            maxRight = thisObject.EOM.getMaxRight();
            maxBottom = thisObject.EOM.getMaxBottom();
            if (maxRight < 100) {
                maxRight = 100;
            }
            if (maxBottom < 50) {
                maxBottom = 50;
            }
            $('#' + thisObject.id).resizable('option', 'minWidth', maxRight);
            $('#' + thisObject.id).resizable('option', 'minHeight', maxBottom);
        },
        resize: function(event, ui){
            thisObject.setWidth(document.getElementById(thisObject.id).offsetWidth);
            thisObject.setHeight(document.getElementById(thisObject.id).offsetHeight);
			thisObject.onResize(document.getElementById(thisObject.id).offsetWidth, document.getElementById(thisObject.id).offsetHeight, thisObject);
        },
        stop: function(event, ui){
            thisObject.setWidth(document.getElementById(thisObject.id).offsetWidth);
            thisObject.setHeight(document.getElementById(thisObject.id).offsetHeight);
			thisObject.onAfterResize(document.getElementById(thisObject.id).offsetWidth, document.getElementById(thisObject.id).offsetHeight, thisObject);
        },
        containment: '#' + this.parentId,
        minWidth: 100,
        minHeight: 50
    });
    
    //make the divs draggable
    $('#' + this.id).draggable({
        handle: '#'+ thisObject.id +'Header',
        containment: '#' + this.parentId,
        drag: function(event, ui){
            thisObject.setTop(document.getElementById(thisObject.id).offsetTop);
            thisObject.setLeft(document.getElementById(thisObject.id).offsetLeft);
			thisObject.onDrag(document.getElementById(thisObject.id).offsetLeft, document.getElementById(thisObject.id).offsetTop, thisObject);
        },
        stop: function(event, ui){
            thisObject.setTop(document.getElementById(thisObject.id).offsetTop);
            thisObject.setLeft(document.getElementById(thisObject.id).offsetLeft);
			thisObject.onDrop(document.getElementById(thisObject.id).offsetLeft, document.getElementById(thisObject.id).offsetTop, thisObject);
        }
    });
    
    // Makes things in the edition area selectable.
    $('#' + this.id).selectable({
        cancel: '',
        selected: function(event, ui){
            var selectedId = $(ui.selected).attr('id');
            var EO = null;
            EO = thisObject.EOM.getEOByHTMLObjectId(selectedId);
            if (EO != -1) {
                if (EO.selected == false) {
                    thisObject.EOM.selectEO(EO.id);
                }
            }
        },
        start: function(event, ui){
            if (event.ctrlKey == false) {
                thisObject.EOM.unselectAll();
            }
            
        },
        unselected: function(event, ui){
            var unselectedId = $(ui.unselected).attr('id');
            var EO = null;
            EO = thisObject.EOM.getEOByHTMLObjectId(unselectedId);
            if (EO != -1) {
                thisObject.EOM.unselectEO(EO.id);
            }
        }
    });
	
    var myCursor = new cursor(this.id);
    
    // Makes the edition area droppable to receive its objects
    $('#' + this.id).droppable({
        tolerance: 'fit',
        hoverClass: 'edition-area-drag-hover',
        drop: function(event, ui){
            // Gets the id of the image being dropped 
            var imageId = $(ui.draggable).attr('id');
            var image = document.getElementById(imageId);
            
            // Continue only if its an image from the toolBox.
            if ((image.tagName == "IMG") && (image.className.indexOf('tool-icon') != -1)) {
                // Creates an Editor Object according to the image of the tool dropped in this edition area.
				// The name of the image is its type.
                var EO = thisObject.EOM.createEO(image.name);
				thisObject.appendObject(EO);

				EO.setInstructionBox(image.instructionBox);
				EO.setPropertyBox(image.propertyBox);
				
                EO.setLeft(myCursor.getLeft(event) - myCursor.getContainerLeft());
                EO.setTop(myCursor.getTop(event) - myCursor.getContainerTop());
				
				// Default Snap to grid
				EO.setSnapTo(thisObject.snapTo.x, thisObject.snapTo.y);
				
                thisObject.EOM.unselectAll();
				
				if (EO.onAppend){
					EO.onAppend();	
				}
            }
        }
    });
	
	/**
	 * Contains the snapTo configuration of this edition area.
	 * The SnapTo is the measurement in pixels of the size of the steps used by the EO movements in the edition area.
	 * The purpose is to ease the alignment of several EOs.
	 * @type {Object} 
	 */
	this.snapTo = {
		x: 10,
		y: 10
	};

	/**
	 * Sets the snapTo of the editionArea and of all EOs there.
	 * @param {Int} x The x coordinate of the snapTo being set.
	 * @param {Int} y The y coordinate of the snapTo being set.
	 */
	this.setSnapGrid = function(x, y){
		// Snap to grid configuration
		$( "#" + id ).draggable('option', 'grid', [ x,y ]);
		this.EOM.setAllEOSnapTo(x, y);
		this.snapTo = {x: x, y: y};
	}
	
	/**
	 * Adds an EO to the editionArea.
	 * @param {Object} EO The EO that is going to be added
	 */    
    this.appendObject = function(EO){
        // Adds an object to the edition area.
        var objectList = EO.objectList;
        var HTMLObject = null;
		this.EOM.registerEO(EO);
		this.EOM.attribIds(EO);
		
        // The div that is going to contain the EO
        var divEO = document.createElement('div');
        divEO.id = EO.id;
		divEO.className = 'edition-area-eo-container';
        
        // Events to be implemented by the tools
        divEO.onmouseover = function(event){
            if (EO.onMouseOver) {
                EO.onMouseOver(event, EO);
            }
        }
        
        divEO.onmouseout = function(event){
            if (EO.onMouseOut) {
                EO.onMouseOut(event, EO);
            }
        }
        
        divEO.onmousedown = function(event){
            if (EO.onMouseDown) {
                EO.onMouseDown(event, EO);
            }
        }
        
        divEO.ondblclick = function(event){
            if (EO.onDblClick) {
                EO.onDblClick(event, EO);
            }
        }
        // end of events
        
        for (var i = 0; i < objectList.length; i++) {
			HTMLObject = EO.getHTMLObject(objectList[i]);
			divEO.appendChild(HTMLObject);		
        }
        
        editionArea.appendChild(divEO);
		this.EOM.setSelectors(EO, EO.selectorIds);	
        
        $('#' + divEO.id).draggable({
            cancel: '',
            containment: '#' + thisObject.id,
			drag: function(event, ui){
				thisObject.EOM.moveSelectedLeft(ui.position.left - EO.getLeft());
				thisObject.EOM.moveSelectedTop(ui.position.top - EO.getTop());
				if (EO.onDrag){
					EO.onDrag(EO.getLeft(), EO.getTop(), EO);
				}
			}, 
			stop: function(event, ui){
				if (EO.onDrop){
					EO.onDrop(EO.getLeft(), EO.getTop(), EO);
				}
			}
        });
		
		this.onAppend(EO, this);
		
		EO.setSnapTo(this.snapTo.x,this.snapTo.x);
    }
    
	/**
	 *  Returns a JSON corresponding to the content of the edition area and all its EOs.
	 *  @return The correspondign JSON object.
	 *  @type {Object}
	 */
    this.contentJSON = function(){
        var JSONObject = this.EOM.EO_JSON();
        return JSONObject;
    }
    
	/**
	 * Sets the left property of the edition area. 
	 * @param {Int} left The left coordinate to be set.
	 */
    this.setLeft = function(left){
        editionAreaBg.style.left = left + 'px';
        if (navigator.appName != "Microsoft Internet Explorer") {
            editionArea.style.left = left + parseInt(editionAreaBg.style.borderLeftWidth) + 'px';
        }
        else {
            editionArea.style.left = left + 'px';
        }
        this.left = left;
    }
		
	/**
	 * Sets the top property of the edition area.
	 * @param {Int} top The top coordinate to be set.
	 */
    this.setTop = function(top){
        editionAreaBg.style.top = top + 'px';
        if (navigator.appName != "Microsoft Internet Explorer") {
            editionArea.style.top = top + parseInt(editionAreaBg.style.borderTopWidth) + 'px';
        }
        else {
            editionArea.style.top = top + 'px';
        }
        this.top = top;
    }
    
	/**
	 * Sets the width of the edition area.
	 * @param {Int} width The width to be set.
	 */
    this.setWidth = function(width){
        editionAreaBg.style.width = width + 'px';
        editionArea.style.width = width + 'px';
        this.width = width;
    }
    
	/**
	 * Sets the height of the edition area.
	 * @param {Int} height The height to be set.
	 */
    this.setHeight = function(height){
        editionAreaBg.style.height = height + 'px';
        editionArea.style.height = height + 'px';
        this.height = height;
    }
	
	/**
	 * Sets the title of the edition area. 
	 * @param {String} title The new title to be set.
	 */	
	this.setTitle = function(title){
		editionAreaHeader.innerHTML = title;
		this.title = title;
	}
    
    this.setLeft(this.left);
    this.setTop(this.top);
    this.setWidth(this.width);
    this.setHeight(this.height);
	
	/**
	 * Changes a property of the edition area to some value
	 * @param {String} propertyName The name of the property to be changed.
	 * @param {Any} value The value to be set to the property.
	 */		
	this.setProperty = function(propertyName, value){
		switch(propertyName){
			case 'height':
				this.setHeight(value);
				break;
			
			case 'width':
				this.setWidth(value);
				break;
				
			case 'top':
				this.setTop(value);
				break;
			
			case 'left':
				this.setLeft(value);
				break;
				
			case 'title':
				this.setTitle(value);
				break; 
			
			case 'cssPath':
				this.setCSS(value);
				break;
			
			default:
				document.write('Property not found');
				break;
		}
	}

	/**
	 * Removes this edition area.
	 */		
	this.remove =  function(){
		this.EOM.removeAllEOs();
		editionArea.parentNode.removeChild(editionArea);
		editionAreaBg.parentNode.removeChild(editionAreaBg);
		editionAreaHeader.parentNode.removeChild(editionAreaHeader);
		delete editionArea;
		delete editionAreaHeader;
		delete editionAreaBg;
		delete this.EOM;
		this.onRemove();
	}

	/**
	 * Sets a new CSS stylesheet to the edition area.
	 * @param {String} path The relative path of the CSS stylesheet file from the APP_HOME folder.
	 */		
	this.setCSS = function(path){
		this.workspace.setCSS(this, path);
	}
	this.setCSS(this.cssPath);	
}
